
#ifndef __CGALTYPES__
#define __CGALTYPES__

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/squared_distance_3.h>
#include <CGAL/aff_transformation_tags.h>
#include <cmath>

// Simple CGAL types
typedef float                                Number;
typedef CGAL::Simple_cartesian<float>        Kernel;
typedef CGAL::Point_3<Kernel>                Point;
typedef CGAL::Vector_3<Kernel>               Vector;
typedef CGAL::Ray_3<Kernel>                  Ray;
typedef CGAL::Triangle_3<Kernel>             Triangle;
typedef CGAL::Segment_3<Kernel>              Segment;
typedef CGAL::Plane_3<Kernel>                Plane;
typedef CGAL::Ray_3<Kernel>                  Ray;
typedef CGAL::Aff_transformation_3<Kernel>   Transformation;

/// An extended bounding box class
struct Bbox : public CGAL::Bbox_3 {
    
    ///Constructors
    Bbox (double x0, double y0, double z0, 
          double x1, double y1, double z1)
        : CGAL::Bbox_3 (x0,y0,z0,x1,y1,z1) {}
    Bbox (const Point& p0, const Point& p1)
      : CGAL::Bbox_3 (p0.x(), p0.y(), p0.z(), p1.x(), p1.y(), p1.z()) {}
    Bbox (const CGAL::Bbox_3& box) 
        : CGAL::Bbox_3 (box) {}
    Bbox () 
        : CGAL::Bbox_3 () {}
    
    /// Point at the center of the bounding box
    inline Point center () {
        return Point ((xmin()+xmax())/2,(ymin()+ymax())/2,(zmin()+zmax())/2);
    }
    
    /// Lengths in the 3 dimensions
    inline double xlen () { return xmax()-xmin();}
    inline double ylen () { return ymax()-ymin();}
    inline double zlen () { return zmax()-zmin();}
    
    /// Min, max and len for a given dimension
    inline double len (int dim) { return dim == 0 ? xlen() : dim == 1 ? ylen() : zlen(); }
    inline double min (int dim) { return dim == 0 ? xmin() : dim == 1 ? ymin() : zmin(); }
    inline double max (int dim) { return dim == 0 ? xmax() : dim == 1 ? ymax() : zmax(); }
    
    /// length of the box diagonal
    inline double diameter () {
        Vector v (xmax()-xmin(),ymax()-ymin(),zmax()-zmin());
        return sqrt (v.x()*v.x()+v.y()*v.y()+v.z()*v.z());
    }
};

/// Returns a normalized (unit) vector.
/// @param v input vector.
/// @return v normalized. 
inline Vector normalize (const Vector& v) {
    double len = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
    return Vector(v[0]/len,v[1]/len,v[2]/len);
}

/// Returns the rotation part of a transformation
/// @param t Input transformation
/// @return Rotation transformation
inline Transformation rotationComponent (const Transformation& t) 
{
    Vector v0 = normalize(Vector (t.m(0,0), t.m(1,0), t.m(2,0)));
    Vector v1 = normalize(Vector (t.m(0,1), t.m(1,1), t.m(2,1)));
    Vector v2 = normalize(Vector (t.m(0,2), t.m(1,2), t.m(2,2)));
    return Transformation (v0.x(), v1.x(), v2.x(),
                           v0.y(), v1.y(), v2.y(),
                           v0.z(), v1.z(), v2.z());
}

/// Returns the translation part of a transformation
/// @param t Input transformation
/// @return Translation transformation
inline Transformation translationComponent (const Transformation& t) 
{
   return Transformation (CGAL::TRANSLATION, Vector (t.m(0,3), t.m(1,3), t.m(2,3)));
}

/// A Print function for transformations.
/// @param os output stream.
/// @param t a Transformation.
inline void print (std::ostream& os, const Transformation& t)
{
    for (int i=0; i < 4; i++) {
      for (int j=0; j < 4; j++) {
          os << t.m(i,j); 
          if (j < 3) os << ", ";
          else os << "\n";
      }
    }
}

#endif
